﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.ComponentModel;
using System.Runtime.Serialization;


namespace XMas
{
    /// <summary>
    /// Randomly choose a child to play, when finish play another with blend
    /// Useful to have different animations with one action, like 3 idles
    /// </summary>
    [Serializable()]
    public class AnimNode_Random : AnimNode_BlendList
    {
        /// <summary>
        /// Data for items in AnimNode_Random
        /// </summary>
        public class Random_Data
        {
            /// <summary>
            /// Weigth to calculate the posibility to selec this child
            /// </summary>
            private float _fRandomWeigth;
            /// <summary>
            /// if can be selected again when animation item end
            /// </summary>
            private bool  _bCanRepeat;


            /// <summary>
            /// Atribute for _fRandomWeigth
            /// <see cref="Random_Data._fRandomWeigth"/>
            /// </summary>
            public float RandomWeigth
            {
                get { return _fRandomWeigth; }
                set { _fRandomWeigth = value; }
            }

            /// <summary>
            /// Atribute for _bCanRepeat
            /// <see cref="Random_Data._bCanRepeat"/>
            /// </summary>
            public bool CanRepeat
            {
                get { return _bCanRepeat; }
                set { _bCanRepeat = value; }
            }


            /// <summary>
            /// Constructor
            /// </summary>
            public Random_Data()
            {
                _fRandomWeigth = 1.0f;
                _bCanRepeat = false;
            }
        }


        #region Fields

        /// <summary>
        /// List with all item random data 
        /// </summary>
        protected List<Random_Data> _RandomDataList;
        /// <summary>
        /// Number of version of this subnode, used in serialization
        /// </summary>
        /// <remarks>
        /// If you add a new value to serialization, you need to increment this float
        /// This is need to have compatibility with olders version of library
        /// <see cref="WriteXml(System.Xml.XmlWriter)"/>
        /// </remarks> 
        protected const float _SerializationVersionRandom = 1.0f;

        #endregion


        #region Properties

        /// <summary>
        /// Atribute for _RandomDataList
        /// <see cref="AnimNode_Random._RandomDataList"/>
        /// </summary>
        [CategoryAttribute("Random"), DescriptionAttribute("List with all item random data")]
        public List<Random_Data> RandomDataList
        {
            get { return _RandomDataList; }
            set { _RandomDataList = value; }
        }

        #endregion


        # region Protected Members

        /// <summary>
        /// Initialization of node, set initial values
        /// </summary>
        /// <param name="ID">Node ID</param>
        protected override void Initialize(System.Guid ID)
        {
            _RandomDataList = new List<Random_Data>();
            base.Initialize(ID);

            _strName = "Random";            
            AddChild("Element 1", 0f);
        }

        /// <summary>
        /// Internal get current value, first try to use reflexion. If no reflexion found use editor value
        /// Override with caution
        /// </summary>
        /// <returns></returns>
        protected override float GetValue()
        {
            // We need a value from 0 to ChildCount -1
            int iResultValue = (int)_fEditorValue;
            // Clamp
            if (iResultValue < 0)
                iResultValue = 0;
            if (iResultValue >= _animNodeItemList.Count)
                iResultValue = _animNodeItemList.Count - 1;

            return (float)iResultValue;
        }

        #endregion


        #region Public members

        /// <summary>
        /// Add new child to this node
        /// </summary>
        /// <param name="ItemText">Child text</param>
        /// <param name="fInitialWeigth">Child initial weigth </param>
        /// <returns>New item</returns>
        public override AnimationNodeItem AddChild(string ItemText, float fInitialWeigth)
        {
            AnimationNodeItem NodeItem = base.AddChild(ItemText, fInitialWeigth);
            if (NodeItem != null && _RandomDataList.Count < _animNodeItemList.Count)
                _RandomDataList.Add(new Random_Data());

            return NodeItem;
        }


        /// <summary>
        /// Remove a child from this node
        /// </summary>
        /// <param name="iIdxNode">Child index </param>
        /// <returns>True if the children has been removed</returns>
        public override bool RemoveChild(int iIdxNode)
        {
            bool bResult = base.RemoveChild(iIdxNode);
            if (bResult && iIdxNode < _RandomDataList.Count)
                _RandomDataList.RemoveAt(iIdxNode);

            return bResult;
        }

        /// <summary>
        /// Called when a AnimNode_Animation child has finish animation
        /// </summary>
        /// <param name="sender">Node that raise event</param>
        public override void OnChildAnimEnd(AnimNode_Animation sender)
        {
            // Get the idx            
            int iIdx = -1;
            for (int i = 0; i < _animNodeItemList.Count; ++i)
            {
                if (_animNodeItemList[i].AnimNodeDestination == sender)
                {
                    iIdx = i;
                    break;
                }
            }
            if (iIdx == -1)
                return;

            // Get the sum of all values to get max
            float fTotalSum = 0;
            for (int i = 0; i < _RandomDataList.Count; ++i)
            {
                if (i != iIdx || _RandomDataList[i].CanRepeat)
                    fTotalSum += _RandomDataList[i].RandomWeigth;
            }

            // Random from 0 to fTotalSum
            Random fixRand = new Random();
            float fObjetive = ((float)fixRand.NextDouble()) * fTotalSum;
            
            // Get the new index
            int iResultIdx = -1;
            for (int i = 0; i < _RandomDataList.Count; ++i)
            {
                if (i != iIdx || _RandomDataList[i].CanRepeat)
                    fObjetive -= _RandomDataList[i].RandomWeigth;
                if (fObjetive <= 0)
                {
                    iResultIdx = i;
                    break;
                }
            }

            // Set the new index
            if (iResultIdx != -1)
            {
                // Set to 0 if hasn't loop
                AnimNode_Animation animNode = (AnimNode_Animation)_animNodeItemList[iResultIdx].AnimNodeDestination;
                if (animNode != null)
                {
                    if (!animNode.Loop)
                        animNode.ResetTime();
                }
                _fEditorValue = iResultIdx;                
            }
        }

        #endregion


        #region Load/Save XML

        /// <summary>
        /// Function to write in XML format, if you overwrite you must call base class first
        /// </summary>
        /// <remarks>
        /// Be carefully with the save order, it must be the same that read order!!
        /// </remarks>
        /// <param name="writer">Writer to store data</param>
        public override void WriteXml(XmlWriter writer)
        {
            base.WriteXml(writer);

            writer.WriteElementString("SerializationVersionRandom", _SerializationVersionRandom.ToString(new System.Globalization.CultureInfo("en-US")));
            writer.WriteStartElement("RandomList", null);
            // Random item list
            foreach (Random_Data itemRandom in _RandomDataList)
            {
                writer.WriteStartElement("RandomItem", null);
                writer.WriteElementString("RandomWeigth", itemRandom.RandomWeigth.ToString(new System.Globalization.CultureInfo("en-US")));
                writer.WriteElementString("CanRepeat", itemRandom.CanRepeat ? "1" : "0");
                writer.WriteEndElement();
            }
            writer.WriteEndElement();                
        }

        /// <summary>
        /// Function to read xml format, if you overwrite you must call base class first
        /// </summary>
        /// <remarks>
        /// Be carefully with the read order, it must be the same that save order!!
        /// </remarks>
        /// <param name="reader">Reader to retrieve data</param>
        public override void ReadXml(XmlReader reader)
        {
            base.ReadXml(reader);

            float SerializationVersionRandom = reader.ReadElementContentAsFloat("SerializationVersionRandom", reader.NamespaceURI);
            // Get random item list
            _RandomDataList = new List<Random_Data>();

            reader.ReadStartElement("RandomList");
            bool bFound = false;
            while (reader.IsStartElement("RandomItem", reader.NamespaceURI))
            {
                Random_Data data = new Random_Data();

                reader.ReadStartElement();
                data.RandomWeigth = reader.ReadElementContentAsFloat("RandomWeigth", reader.NamespaceURI);
                data.CanRepeat = reader.ReadElementContentAsBoolean("CanRepeat", reader.NamespaceURI);
                reader.ReadEndElement();

                _RandomDataList.Add(data);
                bFound = true;
            }
            // </RandomList> only if items found
            if (bFound)
                reader.ReadEndElement();
        }

        #endregion


        /// <summary>
        /// Contructor
        /// </summary>
        public AnimNode_Random()
        {            
        }
    }
}
